/*
 * @Author      : ZhouQiJun
 * @Date        : 2025-08-31 16:38:41
 * @LastEditors : ZhouQiJun
 * @LastEditTime: 2025-09-04 03:51:49
 * @Description : 二次封装绘制交互
 */
import { Transforms, isGeoJson, isGeojsonObj, isValidDomId, isWKT } from '#ol-vue-hooks'
import { arrow, computePosition, flip, offset, shift } from '@floating-ui/dom'
import type { PlainObj, ProjCode } from '@ol/types'
import { isNil, isPlainObject, merge } from 'es-toolkit'
import { isElement, isEmpty } from 'es-toolkit/compat'
import { Collection, Feature, type Map, type MapBrowserEvent } from 'ol'
import { type StopRepeat, debounce, isNumber, randomStr, repeatRun } from 'petite-utils'

import { GeoJSON, WKT } from 'ol/format'
import { type Geometry } from 'ol/geom'
import { Draw, Modify, Pointer, Select, Translate } from 'ol/interaction'
import type { ModifyEvent } from 'ol/interaction/Modify'
import { TranslateEvent } from 'ol/interaction/Translate'
import type { Vector } from 'ol/layer'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import CircleStyle from 'ol/style/Circle'
import Fill from 'ol/style/Fill'
import Stroke from 'ol/style/Stroke'
import type { StyleLike } from 'ol/style/Style'
import Style from 'ol/style/Style'

import {
  type Confirm,
  type ConfirmOptions,
  type DomId,
  type FeatureOptions,
  type GeoType,
  type Id,
  OlDrawI,
} from './OlDrawI'

type Action = ['draw', 'modify', 'translate', 'remove']

type OlDrawOptions = {
  freehand?: boolean
  showToolBar?: boolean
  actions?: Action
  geomTypes?: GeoType[]
  zIndex?: number
  confirmOnRemoveAll?: Confirm | boolean
}
// 绘制图层的 zIndex 设置为一个很大的值（超过9亿），确保在最上层显示
const zIndex = +Math.floor(Number.MAX_SAFE_INTEGER / 1000_0000)

const DATA_PROJ: ProjCode = 'EPSG:4326'

type FeatureData = {
  id: Id
  wkt: string
  geojson: string
  geojsonObj: GeoJSON
}

const olBrandColor = getComputedStyle(document.documentElement).getPropertyValue('--ol-foreground-color') //?? '#00AAFF'
const buttons = [
  {
    name: 'point',
    type: 'Point',
    title: '绘制点',
    icon: (color: string) => {
      return `<svg
  xmlns="http://www.w3.org/2000/svg"
  width="1.5rem"
  height="1.5rem"
  viewBox="0 0 24 24" title="绘制点">
  <path
    fill="${color}"
    stroke="${color}"
    d="M12 6.5A2.5 2.5 0 0 1 14.5 9a2.5 2.5 0 0 1-2.5 2.5A2.5 2.5 0 0 1 9.5 9A2.5 2.5 0 0 1 12 6.5M12 2a7 7 0 0 1 7 7c0 5.25-7 13-7 13S5 14.25 5 9a7 7 0 0 1 7-7m0 2a5 5 0 0 0-5 5c0 1 0 3 5 9.71C17 12 17 10 17 9a5 5 0 0 0-5-5" />
</svg>`
    },
  },
  {
    name: 'line',
    type: 'LineString',
    title: '绘制线',
    icon: (color: string) => {
      return `<svg
xmlns="http://www.w3.org/2000/svg"
width="1.5rem"
height="1.5rem"
viewBox="0 0 24 24">
<path
  fill="${color}"
  stroke="${color}"
  stroke-linecap="round"
  stroke-linejoin="round"
  stroke-width="1"
  d="M4 18a2 2 0 1 0 4 0a2 2 0 1 0-4 0M16 6a2 2 0 1 0 4 0a2 2 0 1 0-4 0M7.5 16.5l9-9" />
</svg>`
    },
  },
  {
    name: 'polygon',
    type: 'Polygon',
    title: '绘制多边形',
    icon: (color: string) => {
      return `<svg
      xmlns="http://www.w3.org/2000/svg"
      width="1.5rem"
      height="1.5rem"
      viewBox="0 0 24 24">
      <path
        fill="${color}"
        stroke-width="1"
        d="m21.934 11.751l-4.75-8.255a.5.5 0 0 0-.434-.25h-9.5a.5.5 0 0 0-.434.25l-4.75 8.255a.5.5 0 0 0 0 .498l4.75 8.255c.09.155.255.25.434.25h9.5a.5.5 0 0 0 .434-.25l4.75-8.255a.5.5 0 0 0 0-.498m-5.473 8.004H7.539L3.077 12L7.54 4.245h8.922L20.923 12z" />
    </svg>`
    },
  },
  {
    name: 'circle',
    type: 'Circle',
    title: '绘制圆',
    icon: (color: string) => {
      return `<svg
    xmlns="http://www.w3.org/2000/svg"
    width="1.5rem"
    height="1.5rem"
    viewBox="0 0 24 24">
    <path
      fill="none"
      stroke="${color}"
      stroke-linecap="round"
      stroke-linejoin="round"
      stroke-width="1"
      d="M3 12c0 -4.97 4.03 -9 9 -9c4.97 0 9 4.03 9 9c0 4.97 -4.03 9 -9 9c-4.97 0 -9 -4.03 -9 -9Z" />
  </svg>`
    },
  },
  {
    name: 'rectangle',
    type: 'Rectangle',
    title: '绘制矩形',
    icon: (color: string) => {
      return `<svg
       xmlns="http://www.w3.org/2000/svg"
       width="1.5rem"
       height="1.5rem"
       viewBox="0 0 24 24">
       <path
         fill="none"
         stroke="${color}"
         stroke-linecap="round"
         stroke-linejoin="round"
         stroke-width="1"
         d="M3 7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
     </svg>`
    },
  },
  {
    name: 'freehand',
    type: 'freehand',
    title: '自由绘制，只有线、多边形和圆支持',
    icon: (color: string) => {
      return `<svg
      xmlns="http://www.w3.org/2000/svg"
      width="1.5rem"
      height="1.5rem"
      viewBox="0 0 32 32">
      <path
        fill="${color}"
        d="M14 2.5a.5.5 0 0 1 .5-.5h1c4.01 0 6.81.25 8.621.702c.903.226 1.602.512 2.085.875c.497.372.794.852.794 1.423c0 .47-.122.948-.52 1.368c-.379.399-.963.696-1.783.926C23.062 7.752 20.25 8 15.5 8s-7.438.252-8.928.669c-.742.208-1.127.441-1.326.652C5.066 9.51 5 9.72 5 10c0 .179.078.387.394.623c.33.247.88.493 1.727.704c1.232.308 3.002.525 5.417.62q-.038.27-.038.553v.446c-2.456-.096-4.301-.318-5.621-.648c-.903-.226-1.602-.512-2.085-.875C4.297 11.05 4 10.571 4 10c0-.47.122-.948.52-1.368c.379-.399.963-.696 1.783-.926C7.939 7.248 10.75 7 15.5 7c4.751 0 7.439-.252 8.928-.669c.742-.208 1.127-.441 1.326-.652c.18-.19.246-.399.246-.679c0-.179-.078-.387-.394-.623c-.33-.247-.88-.493-1.727-.704C22.189 3.25 19.489 3 15.5 3h-1a.5.5 0 0 1-.5-.5m2.476 7.5a2.5 2.5 0 0 0-2.5 2.5v6.731l-1.285-.57a4.49 4.49 0 0 0-5.513 1.55a1.006 1.006 0 0 0 .427 1.491c5.713 2.518 7.633 4.811 8.437 6.54c.502 1.079 1.616 1.93 2.917 1.73l3.634-.56a2.5 2.5 0 0 0 2.046-1.867l1.224-4.92a4.5 4.5 0 0 0-3.651-5.53l-3.237-.521v-4.075A2.5 2.5 0 0 0 16.476 10m-1.5 2.5a1.5 1.5 0 0 1 3 0V17a.5.5 0 0 0 .42.494l3.657.588a3.5 3.5 0 0 1 2.84 4.301l-1.224 4.92a1.5 1.5 0 0 1-1.228 1.121l-3.634.56c-.742.114-1.488-.368-1.858-1.163c-.944-2.03-3.104-4.462-8.941-7.034l-.004-.002l-.002-.005a3.49 3.49 0 0 1 4.284-1.204l1.988.881a.5.5 0 0 0 .703-.457z" />
    </svg>`
    },
  },
  {
    name: 'modify',
    type: 'modify',
    title: '修改要素，按住 alt 可删除顶点',
    icon: (color: string) => {
      return `<svg
      xmlns="http://www.w3.org/2000/svg"
      width="1.5rem"
      height="1.5rem"
      viewBox="0 0 24 24">
      <g
        fill="none"
        stroke="${color}"
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-width="1.1">
        <path
          d="m16.475 5.408l2.117 2.117m-.756-3.982L12.109 9.27a2.1 2.1 0 0 0-.58 1.082L11 13l2.648-.53c.41-.082.786-.283 1.082-.579l5.727-5.727a1.853 1.853 0 1 0-2.621-2.621" />
        <path d="M19 15v3a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h3" />
      </g>
    </svg>`
    },
  },
  {
    name: 'translate',
    type: 'translate',
    title: '移动要素',
    icon: (color: string) => {
      return `<svg
      xmlns="http://www.w3.org/2000/svg"
      width="1.5rem"
      height="1.5rem"
      viewBox="0 0 24 24">
      <path
        fill="${color}"
        d="m12 3l.365-.342l-.365-.39l-.365.39zm-.5 6a.5.5 0 0 0 1 0zm3.865-3.142l-3-3.2l-.73.684l3 3.2zm-3.73-3.2l-3 3.2l.73.684l3-3.2zM11.5 3v6h1V3zm9.5 9l.342.365l.39-.365l-.39-.365zm-6-.5a.5.5 0 0 0 0 1zm3.142 3.865l3.2-3l-.684-.73l-3.2 3zm3.2-3.73l-3.2-3l-.684.73l3.2 3zM21 11.5h-6v1h6zM12 21l.365.342l-.365.39l-.365-.39zm-.5-6a.5.5 0 0 1 1 0zm3.865 3.142l-3 3.2l-.73-.684l3-3.2zm-3.73 3.2l-3-3.2l.73-.684l3 3.2zM11.5 21v-6h1v6zM3 12l-.342.365l-.39-.365l.39-.365zm6-.5a.5.5 0 0 1 0 1zm-3.142 3.865l-3.2-3l.684-.73l3.2 3zm-3.2-3.73l3.2-3l.684.73l-3.2 3zM3 11.5h6v1H3z" />
    </svg>`
    },
  },
  {
    name: 'remove',
    type: 'remove',
    title: '删除要素',
    icon: (color: string) => {
      return `<svg
      xmlns="http://www.w3.org/2000/svg"
      width="1.5rem"
      height="1.5rem"
      viewBox="0 0 20 20">
      <path
        fill="${color}"
        d="M8.5 4h3a1.5 1.5 0 0 0-3 0m-1 0a2.5 2.5 0 0 1 5 0h5a.5.5 0 0 1 0 1h-1.054l-1.194 10.344A3 3 0 0 1 12.272 18H7.728a3 3 0 0 1-2.98-2.656L3.554 5H2.5a.5.5 0 0 1 0-1zM5.741 15.23A2 2 0 0 0 7.728 17h4.544a2 2 0 0 0 1.987-1.77L15.439 5H4.561zM8.5 7.5A.5.5 0 0 1 9 8v6a.5.5 0 0 1-1 0V8a.5.5 0 0 1 .5-.5M12 8a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z" />
    </svg>`
    },
  },
  {
    name: 'complete',
    type: 'complete',
    title: '完成',
    icon: (color: string) => {
      return `<svg
       xmlns="http://www.w3.org/2000/svg"
       width="1.5rem"
       height="1.5rem"
       viewBox="0 0 24 24">
       <g fill="none" fill-rule="evenodd">
         <path
           d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
         <path
           fill="${color}"
           d="M6 2a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6.414A2 2 0 0 0 19.414 5L17 2.586A2 2 0 0 0 15.586 2zm0 2h9.586L18 6.414V20H6zm10.238 6.793a1 1 0 1 0-1.414-1.414l-4.242 4.243l-1.415-1.415a1 1 0 0 0-1.414 1.414l2.05 2.051a1.1 1.1 0 0 0 1.556 0l4.88-4.879Z" />
       </g>
     </svg>`
    },
  },
  {
    name: 'removeAll',
    type: 'removeAll',
    title: '删除所有',
    icon: (color: string) => {
      return `<svg  xmlns="http://www.w3.org/2000/svg"
            width="1.5rem"
            height="1.5rem"
            viewBox="0 0 1024 1024"><path fill="${color}" d="M160 256H96a32 32 0 0 1 0-64h256V95.936a32 32 0 0 1 32-32h256a32 32 0 0 1 32 32V192h256a32 32 0 1 1 0 64h-64v672a32 32 0 0 1-32 32H192a32 32 0 0 1-32-32zm448-64v-64H416v64zM224 896h576V256H224zm192-128a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32m192 0a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32"></path></svg>`
    },
  },
]
type ElementOf<T extends readonly unknown[]> = T[number]

const DEFAULT_ACTIONS = ['remove', 'modify', 'translate', 'complete'] as const
const TITLE_MAP: Record<ElementOf<typeof DEFAULT_ACTIONS>, string> = {
  remove: '移除选中的几何图形或者所有图形',
  modify: '修改几何图形',
  translate: '平移几何图形',
  complete: '完成绘制',
} as const

const DEFAULT_CONFIRM = {
  cancel: '取消',
  ok: '确定',
  confirmContent: '确定移除所有要素吗？',
}
const genRemoveAllConfirmDom = ({
  cancel = '取消',
  ok = '确定',
  confirmContent = '确定移除所有要素吗？',
}: Confirm = {}) => {
  //console.log({ cancel, ok, confirmContent })
  return `<div class="confirm-text">${confirmContent}</div>
<div class="actions">
  <button class="cancel button">${cancel}</button>
  <button class="ok button primary">${ok}</button>
</div>
<div class="arrow"></div>`
}

export default class OlDraw extends Pointer implements OlDrawI {
  private actions_ = ['draw', ...DEFAULT_ACTIONS]
  private actionButtons_: string[] = []
  private geomTypes_ = ['Point', 'LineString', 'Polygon'] //, 'Circle']
  private showFreehandGeomTypes_ = ['LineString', 'Polygon', 'Circle', 'freehand']
  private hiddenFreehandActions_ = ['modify', 'translate', 'Point']
  private freehand_ = false
  private showToolBar_ = true
  private source_: VectorSource<Geometry> = new VectorSource()
  private layer_: Vector<VectorSource<Geometry>> = new VectorLayer()
  private hasAddedLayer_ = false
  private draw_: Draw | null = null
  private modify_: Modify | null = null
  private translate_: Translate | null = null
  private select_: Select | null = null
  private mapProj_: ProjCode | null = null
  private selected: FeatureData[] = []
  private enableFreehand_ = false
  private drawingType_: GeoType = 'None'
  private toolBarContainer: HTMLDivElement | null = null
  private confirmDom_ = genRemoveAllConfirmDom()
  private confirm_: Confirm = DEFAULT_CONFIRM

  constructor(options: OlDrawOptions = {}, pointerOptions?: ConstructorParameters<typeof Pointer>) {
    // 创建一个按钮元素
    super({
      handleEvent: () => {
        return true
      },
      ...pointerOptions,
    })

    // 修改现有的要素
    this.source_.on('changefeature', () => {
      //console.log('changefeature')
      this.enableBtn('complete', true, TITLE_MAP['complete'])
    })

    // 添加新要素
    this.source_.on('addfeature', () => {
      DEFAULT_ACTIONS.forEach((action) => {
        //if (action !== 'complete') {
        this.enableBtn(action, true, TITLE_MAP[action])
        //}
      })
    })
    // 移除要素
    this.source_.on('removefeature', () => {
      const hasFeature = this.source_.getFeatures().length > 0
      console.log({ hasFeature }, 'zqj removeFeature')
      if (!hasFeature) {
        this.enableBtn('modify', false, '')
        this.enableBtn('remove', false, '')
        this.enableBtn('translate', false, '')
      }
    })

    this.freehand_ = options?.freehand !== false
    this.showToolBar_ = options?.showToolBar !== false

    if (options?.confirmOnRemoveAll !== false) {
      if (isPlainObject(options.confirmOnRemoveAll)) {
        this.confirm_ = merge(this.confirm_, options?.confirmOnRemoveAll)
      }
      if (this.confirmDom_ !== genRemoveAllConfirmDom(this.confirm_)) {
        this.confirmDom_ = genRemoveAllConfirmDom(this.confirm_)
      }
    } else {
      this.confirmDom_ = ''
    }

    if (!isNil(options?.actions)) {
      this.actions_ = options.actions
    }
    if (Array.isArray(options.geomTypes)) {
      this.geomTypes_ = options.geomTypes
    }

    if (!isNil(options?.zIndex) && isNumber(options?.zIndex)) {
      //  传入了 zIndex 重新设置图层
      this.layer_ = new VectorLayer({
        source: this.source_,
        zIndex: options.zIndex,
        className: this.getClassName(options.zIndex),
      })
    } else {
      const z = zIndex + 1
      this.layer_ = new VectorLayer({
        source: this.source_,
        zIndex: z,
        className: this.getClassName(z),
      })
    }

    const addDrawBar = (times: number, stop: StopRepeat) => {
      const map = this.getMap()
      if (map) {
        stop()
        this.mapProj_ = this.getMap()?.getView()?.getProjection()?.getCode?.() as ProjCode
        if (this.showToolBar_) {
          console.log('添加toolBar')
          this.appendToolBar(map)
        }
        if (this.confirmDom_) {
          this.appendConfirm()
        }
        const debounceOnPointerMove = debounce(this.onPointerMove.bind(this), 150)
        map.on('pointermove', debounceOnPointerMove)
      }
    }
    repeatRun(addDrawBar, { interval: 50, immediate: true })
  }

  addFeature(wktOrJson: string | PlainObj, options: FeatureOptions, id?: Id): boolean {
    if (!this.hasAddedLayer_) {
      this.addLayer(this.getMap())
    }
    let _wktOrJson = ''
    if (isPlainObject(wktOrJson)) {
      if (!isGeojsonObj(wktOrJson)) {
        return false
      }
      _wktOrJson = JSON.stringify(wktOrJson)
    } else {
      _wktOrJson = wktOrJson
    }

    if (isWKT(_wktOrJson)) {
      const feature = new WKT().readFeature(wktOrJson, options)
      if (!feature.getId()) {
        feature.setId(id ?? this.genId())
      }
      this.source_.addFeature(feature)
      return true
    }
    if (isGeoJson(_wktOrJson)) {
      const feature = new GeoJSON().readFeature(wktOrJson, options)
      if (!feature.getId()) {
        feature.setId(id ?? this.genId())
      }
      this.source_.addFeature(feature)
      return true
    }
    // TODO 支持 WKB
    //if(isWKB()){}
    return false
  }
  enableDraw(type: GeoType, freehand: boolean = false): void {
    console.log({ type, freehand })
    if (!type) return
    if (type === 'None') {
      this.draw_ = null
      return
    }
    this.setSelectedBtn(type, true)
    if (this.showFreehandGeomTypes_.includes(type)) {
      this.setSelectedBtn('freehand', freehand)
    }
    if (!this.hasAddedLayer_) {
      this.addLayer(this.getMap())
    }
    this.disableDraw()
    this.disableModify()
    this.disableSelect()
    this.disableTranslate()
    this.draw_ = new Draw({
      type,
      freehand,
      source: this.source_,
    })
    this.getMap()?.addInteraction(this.draw_)
    this.draw_.on('drawend', (event) => {
      this.dispatchEvent(event)
      this.emitFeatureChange(event.feature)
    })
  }
  disableDraw() {
    if (this.draw_) {
      this.getMap()?.removeInteraction(this.draw_)
      this.draw_ = null
    }

    if (this.drawingType_ !== 'None') {
      this.geomTypes_.forEach((type) => {
        this.setSelectedBtn(type, false)
      })
      this.setSelectedBtn('freehand', false)
    }
  }
  enableModify(id?: Id | Id[], style?: StyleLike): void {
    this.disableDraw()
    this.disableTranslate()
    this.enableSelect()
    if (this.modify_) {
      this.modify_.setActive(true)
      this.setSelectedBtn('modify', true)
      return
    }
    this.modify_ = new Modify({
      features: this.select_?.getFeatures(),
    })
    this.getMap()?.addInteraction(this.modify_)
    this.modify_.on('modifyend', (event: ModifyEvent) => {
      const feat = this.getFirstFeature(event)
      this.dispatchEvent(event)
      if (feat) {
        this.emitFeatureChange(feat)
      }
    })
  }
  disableModify(id?: Id | Id[], style?: StyleLike): void {
    if (this.modify_) {
      this.modify_.setActive(false)
    }
    this.setSelectedBtn('modify', false)
  }

  enableTranslate(): void {
    this.disableDraw()
    this.disableModify()
    this.enableSelect()
    this.setSelectedBtn('translate', true)
    if (this.translate_) {
      this.translate_.setActive(true)
      return
    }

    this.translate_ = new Translate({ features: this.select_?.getFeatures() })
    this.translate_.on('translateend', (event: TranslateEvent) => {
      this.dispatchEvent(event)
      const { coordinate, startCoordinate, features } = event
      const featuresData = this.convertFeaturesToData(features)
      const endCoord4326 = Transforms.point(coordinate, this.mapProj_!, 'EPSG:4326')
      const endCoord3857 = Transforms.point(coordinate, this.mapProj_!, 'EPSG:3857')
      const startCoord4326 = Transforms.point(startCoordinate, this.mapProj_!, 'EPSG:4326')
      const startCoord3857 = Transforms.point(startCoordinate, this.mapProj_!, 'EPSG:3857')
      this.emitMoveFeatures({
        startCoord4326,
        endCoord4326,
        startCoord3857,
        endCoord3857,
        featuresData,
        originEvent: event,
      })
    })
    this.getMap()?.addInteraction(this.translate_)
  }
  disableTranslate() {
    if (this.translate_) {
      // this.getMap()?.removeInteraction(this.translate_)
      // this.translate_ = null
      this.translate_.setActive(false)
    }
  }
  enableSelect(idsOrFeatures?: Id[] | Feature[]) {
    this.disableDraw()
    //if (this.select_) {
    //  this.select_.setActive(true)
    //  return
    //}
    const feats: Feature[] = []
    if (Array.isArray(idsOrFeatures)) {
      idsOrFeatures.forEach((item) => {
        if (item instanceof Feature) {
          feats.push(item)
        } else {
          const feat = this.source_?.getFeatureById(item)
          if (feat) {
            feats.push(feat)
          }
        }
      })
    }
    if (feats.length) {
      // 有选中的要素，启用编辑
      this.enableModify()
      this.setSelectedBtn('modify', true)
    }
    const fillColor = 'rgba(218,228,194,0.5)'
    const strokeColor = 'rgba(255, 204, 51, 0.9)'
    this.select_ = new Select({
      layers: [this.layer_],
      features: new Collection(feats),
      style: new Style({
        fill: new Fill({
          color: fillColor,
        }),
        stroke: new Stroke({
          color: strokeColor,
          width: 4,
        }),
        image: new CircleStyle({
          radius: 8,
          fill: new Fill({
            color: fillColor,
          }),
        }),
      }),
      hitTolerance: 6,
    })
    const convert = (feat: Feature) => {
      const { wkt, id } = this.convertWKT(feat)
      const { geojson, geojsonObj } = this.convertGeojson(feat)
      return {
        wkt,
        id,
        geojson,
        geojsonObj,
      }
    }
    this.select_.on('select', (event) => {
      this.dispatchEvent(event)
      const deselected = event.deselected.map(convert)
      //@ts-ignore
      this.selected = event.selected.map(convert)
      //@ts-ignore
      this.dispatchEvent({ selected: this.selected, deselected, type: 'selectchange' })
    })
    this.getMap()?.addInteraction(this.select_)
  }

  disableSelect(idsOrFeatures?: Id[] | Feature[]) {
    if (!this.select_) return
    if (Array.isArray(idsOrFeatures)) {
      idsOrFeatures.forEach((item) => {
        let feat: Feature | null
        if (item instanceof Feature) {
          feat = item
        } else {
          feat = this.source_.getFeatureById(item)
        }
        if (feat) {
          this.select_?.getFeatures().remove(feat)
        }
      })
    } else {
      this.select_.getFeatures().clear()
    }

    if (!this.select_.getFeatures().getLength()) {
      this.disableModify()
      this.setSelectedBtn('modify', false)
      this.getMap()?.removeInteraction(this.select_)
      this.select_ = null
    }
  }

  /**
   * 开启自由绘制
   */
  enableFreehand() {
    if (!this.showFreehandGeomTypes_.includes(this.drawingType_)) {
      return false
    }
    this.enableFreehand_ = true
    this.dispatchEvent({
      type: 'freehandchange',
      //@ts-ignore
      active: true,
    })
    this.setSelectedBtn('freehand', true)
    return true
  }
  disableFreehand() {
    this.enableFreehand_ = false
    this.dispatchEvent({
      type: 'freehandchange',
      //@ts-ignore
      active: false,
    })
    this.setSelectedBtn('freehand', false)
    return true
  }

  private enableSnap() {
    // TODO 似乎不需要 Snap
  }
  private disableSnap() {}

  completeDraw() {
    this.onComplete()
  }

  removeFeatures(id: Id | Id[]): boolean {
    let idList: Id[] = []
    if (!id) return false
    if (Array.isArray(id) && id.length === 0) return false
    if (!Array.isArray(id)) {
      idList.push(id)
    } else {
      idList = id
    }
    const removed = this.convertRemoveFeatures(idList)
    const selectedSize = this.selected.length
    idList.forEach((id) => {
      const feat = this.source_.getFeatureById(id)!
      this.source_.removeFeature(feat)
      this.select_?.getFeatures().remove(feat)
      this.selected = this.selected.filter((item) => item.id !== id)
    })
    const remain = this.source_.getFeatures().map((feat) => {
      const { wkt, id: id_ } = this.convertWKT(feat!)
      const { geojson, geojsonObj } = this.convertGeojson(feat!)
      return {
        wkt,
        id: id_,
        geojson,
        geojsonObj,
      }
    })

    const newSelectedSize = this.selected.length
    if (newSelectedSize !== selectedSize) {
      //@ts-ignore
      this.dispatchEvent({ selected: this.selected, deselected: [], type: 'selectchange' })
    }
    //@ts-ignore
    this.emitRemoveFeatures(removed, remain)
    return true
  }
  /**
   * 移除所有要素
   */
  removeAllFeatures(options?: ConfirmOptions) {
    if (this.confirmDom_ === '') {
      return this.onRemoveAll()
    }

    if (isNil(options?.trigger)) {
      // NOTE 需要二次确认，就需要传递 trigger
      console.error('二次确认需要传递 trigger 参数，通常是打开二次确认弹窗的按钮，比如全部移除按钮')
      return Promise.resolve(false)
    }

    let confirm: Confirm = this.confirm_
    let confirmDom_ = this.confirmDom_
    const c = options?.confirm as Confirm
    const hasConfirm = isPlainObject(c) && !isEmpty(c)
    let _id
    if (hasConfirm) {
      _id = c?.id as DomId
      confirm = merge(confirm, c)
    }
    const confirmDomT = genRemoveAllConfirmDom(confirm)
    if (this.confirmDom_ !== confirmDomT) {
      confirmDom_ = confirmDomT //genRemoveAllConfirmDom(options.confirm)
      _id = (_id || this.genId()) as DomId
    }

    if (_id) {
      this.appendConfirm(_id, confirmDom_)
    }
    return this.openConfirm({
      ...options,
      confirm: {
        ...c,
        id: _id,
      },
    }).then((isOk) => {
      if (isOk) {
        return this.onRemoveAll()
      }
      return false
    })
  }

  private onComplete() {
    const allFeatures = this.source_.getFeatures()
    const allData = this.convertFeaturesToData(allFeatures)
    this.dispatchEvent({
      type: 'completedraw',
      // @ts-ignore
      allData,
      allFeatures,
    })
    this.enableBtn('complete', false, '')

    this.disableDraw()
    this.disableModify()
    this.disableTranslate()
    this.disableSelect()
  }

  private onRemoveAll() {
    this.disableSelect()
    // TODO 参数是什么作用
    this.source_.clear()
    this.selected = []
    //@ts-ignore
    this.dispatchEvent({
      type: 'removeAllFeatures',
    })
    this.completeDraw()
    this.enableBtn('modify', false, '')
    this.enableBtn('remove', false, '')
    this.enableBtn('translate', false, '')
    return Promise.resolve(true)
  }

  private appendToolBar(map: Map) {
    if (!map?.getTargetElement()) return
    const btnContainer = document.createElement('div')
    btnContainer.classList.add('ol-draw-container')
    btnContainer.classList.add('ol-control')
    btnContainer.classList.add('tool-bar')
    btnContainer.style = 'pointer-events: auto;'

    const controlContainer = map.getTargetElement().querySelector('.ol-overlaycontainer-stopevent')
    const _types = buttons.filter((btn) => this.geomTypes_.includes(btn.type))
    if (this.freehand_) {
      _types.push(buttons.find((btn) => btn.name === 'freehand')!)
    }
    const _actions = buttons.filter((btn) => this.actions_.includes(btn.name))
    this.actionButtons_ = _actions.map((item) => item.name)
    const _buttons = [..._types, ..._actions]
    _buttons.forEach((btn) => {
      const btnElement = document.createElement('button')
      btnElement.classList.add('ol-draw-btn')
      if (['freehand'].includes(btn.name)) {
        // NOTE 自由绘制按钮默认隐藏
        btnElement.classList.add('hidden')
      }

      const action = btn.name as ElementOf<typeof DEFAULT_ACTIONS>
      const isDefaultAction = DEFAULT_ACTIONS.includes(action)
      if (isDefaultAction) {
        if (action === 'complete') {
          btnElement.disabled = true
          btnElement.title = ''
        } else if (this.source_.getFeatures().length) {
          // 四个操作默认禁用
          btnElement.disabled = false
          btnElement.title = TITLE_MAP[action]
        } else {
          btnElement.disabled = true
          btnElement.title = ''
        }
      }
      btnElement.innerHTML = btn.icon(olBrandColor)
      btnElement.title = btn.title
      btnElement.dataset.type = btn.type
      btnContainer.appendChild(btnElement)
    })
    controlContainer!.appendChild(btnContainer)
    btnContainer.addEventListener('click', (event: MouseEvent) => {
      const target = event.target as HTMLElement
      const btn = target.closest('button')
      if (!btn) return
      const type = btn.dataset.type as string
      console.log({ type, event }, 'zqj')

      // 先修改 toolBar 按钮样式
      this.updateToolbar(type)
      // 然后记录当前绘制的集合图形
      if (this.geomTypes_.includes(type)) {
        // 记录绘制的类型
        this.drawingType_ = type as GeoType
      }

      // 再启用交互
      if (this.geomTypes_.includes(type as GeoType) || type === 'freehand') {
        this.enableDraw(this.drawingType_, this.enableFreehand_)
      } else if (type === 'modify') {
        this.enableModify()
      } else if (type === 'translate') {
        this.enableTranslate()
      } else if (type === 'remove') {
        // 点击删除按钮
        const idList = this.selected.map((item) => item.id).filter(Boolean)
        if (idList.length) {
          // 移除选中的要素
          this.removeFeatures(idList!)
        } else {
          // 移除所有要素
          this.removeAllFeatures()
        }
      } else if (type === 'complete') {
        // 完成
        this.selected = []
        this.onComplete()
      }
    })
    this.toolBarContainer = btnContainer
  }
  private addLayer(map: Map | null) {
    if (isNil(map)) return
    map.addLayer(this.layer_)
    this.hasAddedLayer_ = true
  }
  private genId() {
    return randomStr(10, 10)
  }
  private getClassName(zIndex: number) {
    return `ol-layer draw-layer vector-layer z-index-is-${zIndex}`
  }

  private emitFeatureChange(feat: Feature) {
    const { wkt, id } = this.convertWKT(feat)
    const { geojson, geojsonObj } = this.convertGeojson(feat)
    this.dispatchEvent({
      type: 'changefeature',
      // @ts-ignore
      geojson,
      geojsonObj,
      wkt,
      id,
    })
  }

  private emitMoveFeatures(data: Record<string, unknown>) {
    // @ts-ignore
    this.dispatchEvent({
      type: 'moveend',
      ...data,
    })
  }

  private emitRemoveFeatures(removed: FeatureData[], remain: FeatureData[]) {
    this.dispatchEvent({
      type: 'removefeature',
      //@ts-ignore
      removed,
      remain,
    })
  }

  private getFirstFeature(event: ModifyEvent) {
    const { features } = event
    const feats = features.getArray()
    const [feat] = feats
    return feat
  }
  private convertWKT(feature: Feature) {
    const { feature: feat, id } = this.setFeatureId(feature)
    const wkt = new WKT().writeFeature(feat, {
      dataProjection: DATA_PROJ,
      featureProjection: this.mapProj_!,
    })
    return { wkt, id }
  }
  private setFeatureId(feature: Feature) {
    let id
    if (!feature.getId()) {
      id = this.genId()
      feature.setId(id)
    } else {
      id = feature.getId()
    }
    return {
      id,
      feature,
    }
  }
  private convertGeojson(feature: Feature) {
    const { feature: feat, id } = this.setFeatureId(feature)
    const geojsonObj = new GeoJSON().writeFeatureObject(feat, {
      dataProjection: DATA_PROJ,
      featureProjection: this.mapProj_!,
    })
    const geojson = new GeoJSON().writeFeature(feat, {
      dataProjection: DATA_PROJ,
      featureProjection: this.mapProj_!,
    })
    return {
      id,
      geojsonObj,
      geojson,
    }
  }

  private convertRemoveFeatures(idList: Id[]) {
    return idList.map((id) => {
      const feat = this.source_.getFeatureById(id)
      const { wkt, id: id_ } = this.convertWKT(feat!)
      const { geojson, geojsonObj } = this.convertGeojson(feat!)
      return {
        wkt,
        id: id_,
        geojson,
        geojsonObj,
      }
    })
  }
  private convertFeaturesToData(feats: Collection<Feature> | Array<Feature>) {
    let list = []
    if (Array.isArray(feats)) {
      list = feats
    } else {
      list = feats.getArray()
    }
    return list.map((feat) => {
      const { wkt, id: id_ } = this.convertWKT(feat!)
      const { geojson, geojsonObj } = this.convertGeojson(feat!)
      return {
        wkt,
        id: id_,
        geojson,
        geojsonObj,
      }
    })
  }

  private updateToolbar(type: string) {
    // 显示自由绘制的类型
    const shouldShowFreehand = this.showFreehandGeomTypes_.includes(type)
    if (!shouldShowFreehand) {
      this.enableFreehand_ = false
      this.setSelectedBtn('freehand', false)
    }
    if (shouldShowFreehand) {
      this.showBtn('freehand', true)
    }
    if (this.hiddenFreehandActions_.includes(type)) {
      this.showBtn('freehand', false)
    }

    // 绘制、平移
    if (this.geomTypes_.includes(type)) {
      // 绘制
      const types = this.geomTypes_.filter((item) => item !== type)
      types.push(...this.actionButtons_)
      this.setSelectedBtn(type, true)
      types.forEach((type) => {
        this.setSelectedBtn(type, false)
      })
    } else if (type === 'freehand') {
      // 自由绘制按钮
      const selected = this.queryBtn('freehand').classList.contains('selected')
      this.enableFreehand_ = !selected
      this.setSelectedBtn(type, !selected)
    } else if (['complete'].includes(type)) {
      // 点击保存后，取消全部选中
      const types = [...this.geomTypes_, ...this.actionButtons_]
      types.forEach((type) => {
        this.setSelectedBtn(type, false)
      })
    } else if (type !== 'remove') {
      // 除移除按钮外，点击某个按钮，其他按钮都取消选中
      this.setSelectedBtn(type, true)
      const types = [...this.geomTypes_, ...this.actionButtons_].filter((t) => t !== type)
      types.forEach((type) => {
        this.setSelectedBtn(type, false)
      })
    }
  }

  private showBtn(type: string, isShow: boolean) {
    const btn = this.queryBtn(type)
    if (isShow) {
      btn?.classList.remove('hidden')
    } else {
      btn?.classList.add('hidden')
    }
  }

  private setSelectedBtn(type: string, isActive: boolean) {
    const btn = this.queryBtn(type)
    if (isActive) {
      btn.classList.remove('hidden')
      btn?.classList.add('selected')
    } else {
      btn?.classList.remove('selected')
    }
  }

  private enableBtn(type: string, enable: boolean, title: string) {
    const btn = this.queryBtn(type)
    if (!btn) return
    btn.disabled = enable === false
    btn.title = title
  }

  private onPointerMove(evt: MapBrowserEvent<MouseEvent>) {
    const isSelectActive = this.select_?.getActive()
    if (evt.dragging || !isSelectActive) {
      return
    }
    const map = evt.map!
    // console.log(evt)
    // 获取当前鼠标位置的像素坐标
    const pixel = map.getEventPixel(evt.originalEvent)
    // console.log(pixel)
    // mousePosition.x = evt.coordinate[0]
    // mousePosition.y = evt.coordinate[1]
    //mousePosition.x = pixel[0].toFixed(2)
    //mousePosition.y = pixel[1].toFixed(2)
    // 检查当前位置是否有要素
    const hit = map.hasFeatureAtPixel(pixel)
    map.getTargetElement().style.cursor = hit ? 'pointer' : ''
  }
  private openConfirm(options?: ConfirmOptions) {
    let _trigger = this.queryBtn('remove') as HTMLElement
    if (!isNil(options?.trigger) && isElement(options?.trigger)) {
      _trigger = options?.trigger
    }
    const overlayContainer = this.getOverlayContainer()!
    let popup = overlayContainer.querySelector<HTMLElement>('.remove-all-confirm')!
    const confirm = options?.confirm
    const isObj = isPlainObject(confirm)
    if (isObj && !isNil(confirm?.id) && isValidDomId(confirm.id)) {
      popup = overlayContainer.querySelector('#' + confirm.id)!
    }
    let text = '确定移除所有要素吗？'
    if (isObj && !isNil(confirm?.confirmContent)) {
      text = confirm?.confirmContent
    }
    const okBtn = popup.querySelector<HTMLButtonElement>('.ok')
    const cancelBtn = popup.querySelector<HTMLButtonElement>('.cancel')
    const textEl = popup.querySelector('.confirm-text')
    const arrowEl = popup.querySelector<HTMLElement>('.arrow')!
    if (!isElement(_trigger)) return Promise.resolve(false)
    return showConfirm({
      trigger: _trigger,
      confirmContent: text,
      //  placement: 'top',
      //  placement: 'right',
      //  placement: 'bottom',
      //  placement: 'left',
      //  placement: 'top-start',
      //  placement: 'right-start',
      //  placement: 'right-center',
      placement: 'right', // 同 right-center
      //  placement: 'right-end',
    })

    function showConfirm({
      trigger,
      confirmContent = '确定要执行此操作吗？',
      placement = 'top',
    }: Confirm = {}) {
      textEl!.textContent = confirmContent
      popup.style.display = 'block'
      popup.setAttribute('aria-hidden', 'false')

      const updatePos = () => {
        computePosition(trigger!, popup, {
          placement,
          middleware: [offset(8), flip(), shift({ padding: 6 }), arrow({ element: arrowEl })],
        }).then(({ x, y, placement, middlewareData }) => {
          popup.style.left = `${Math.round(x)}px`
          popup.style.top = `${Math.round(y)}px`

          // 让 CSS 根据 placement 改箭头边框
          popup.dataset.placement = placement

          // 箭头定位
          // @ts-expect-error
          const { x: ax, y: ay } = middlewareData.arrow
          const basePlacement = placement.split('-')[0] as string
          Object.assign(arrowEl.style, {
            left: !isNil(ax) ? `${ax}px` : '',
            top: !isNil(ay) ? `${ay}px` : '',
            right: '',
            bottom: '',
          })

          const staticSide = {
            top: 'bottom',
            right: 'left',
            bottom: 'top',
            left: 'right',
          }[basePlacement]
          //@ts-expect-error
          arrowEl.style[staticSide] = '-5px'
        })
      }

      updatePos()

      return new Promise((resolve) => {
        const close = (val: boolean) => {
          popup.style.display = 'none'
          popup.setAttribute('aria-hidden', 'true')
          resolve(val)
        }

        if (okBtn) {
          okBtn.onclick = () => close(true)
        }
        if (cancelBtn) {
          cancelBtn.onclick = () => close(false)
        }

        const onDocClick = (e: MouseEvent) => {
          if (!popup.contains(e.target as Node) && e.target !== trigger) {
            close(false)
            document.removeEventListener('mousedown', onDocClick)
          }
        }
        setTimeout(() => document.addEventListener('mousedown', onDocClick), 0)
      })
    }
  }

  private appendConfirm(id?: DomId, confirmInnerHtml?: string) {
    const overlayContainer = this.getOverlayContainer()
    if (!overlayContainer) return
    // <div class="remove-all-confirm" role="dialog" aria-modal="true" aria-hidden="true">
    const confirmContainer = document.createElement('div')
    // NOTE 不设置行内样式，鼠标样式不生效
    confirmContainer.style = 'pointer-events: auto;'
    confirmContainer.classList.add('remove-all-confirm')
    if (!isNil(id) && isValidDomId(id)) {
      if (document.getElementById(id)) {
        // 已经存在，不再插入
        return
      }
      confirmContainer.id = id
    }
    confirmContainer.innerHTML = confirmInnerHtml ?? this.confirmDom_
    overlayContainer.append(confirmContainer)
  }

  private getOverlayContainer() {
    const map = this.getMap()
    if (!map?.getTargetElement()) return
    const container = map.getTargetElement().querySelector('.ol-overlaycontainer-stopevent')
    return container
  }

  private queryBtn(type: string) {
    return this.toolBarContainer?.querySelector(`[data-type="${type}"]`) as HTMLButtonElement
  }
}
